//This code was taken from http://stackoverflow.com/questions/3929884/problem-with-priorityqueue-in-c

#include "Queue.h"

// Instantiates a new queue.
PQueue *queue_new(void) {
    PQueue *pq = malloc(sizeof(PQueue));
    if (pq == NULL) {
        perror("queue_new");
        exit(EXIT_FAILURE);
    }
    pq->head = pq->tail = NULL;
    pq->elements = 0;
    return pq;
}

// Frees up the queue provided.
void queue_free(PQueue *pq) {
    ELEMENT *this, *save;
    this = pq->head;
    while(this!= NULL) {
        save = this;
        this = this->next;
        /*if (save->data != NULL)
        {
            free(save->data); //Took out responsibility due to core dumps.
        }*/
        free(save);
    }
    free(pq);
}

// 'Pushes' a new value into the priority queue with the given priority.
void queue_enqueue(PQueue *pq, void *data, int priority) {
    ELEMENT *newelement;
    newelement = calloc(1,sizeof(ELEMENT));
    if (newelement == NULL) {
        perror("queue_add");
        exit(EXIT_FAILURE);
    }
    newelement->data = data;
    newelement->priority = priority;
    newelement->next = NULL;

    // Inserting into empty list.
    if (pq->elements == 0) {
        pq->head = pq->tail = newelement;
        pq->elements = 1;
        return;
    }

    // Inserting beyond tail.
    if (pq->tail->priority <= priority) {
        pq->tail->next = newelement;
        pq->tail = newelement;
        pq->elements++;
        return;
    }

    // Inserting before head.
    if (pq->head->priority > priority) {
        newelement->next = pq->head;
        pq->head = newelement;
        pq->elements++;
        return;
    }

    // Otherwise, we're inserting somewhere in the middle.
    ELEMENT *ptr = pq->head;
    while (ptr->next->priority <= priority)
        ptr = ptr->next;
    newelement->next = ptr->next;
    ptr->next = newelement;
    pq->elements++;
}

// 'Pops' off the next value in the queue.
void* queue_dequeue(PQueue *pq) {
    if (pq->elements == 0)        // added, just in case.
        return NULL;
    void* item = pq->head->data;
    pq->head = pq->head->next;
    pq->elements--;
    return item;
}

// Returns the front element without removing it.
void* queue_front(PQueue *pq)
{
    if (pq->elements == 0)
        return NULL;
    void* item = pq->head->data;
    return item;
}

// Returns whether or not the queue has any more elements.
int queue_has_next(PQueue *pq) {
    return (pq->elements > 0);    // better, IMNSHO.
}

// Returns the queue size.
int queue_size(PQueue *pq)
{
    return pq->elements;
}
